package com.hup.minicontroller.common.hardware;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
import android.annotation.SuppressLint;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.graphics.Path;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;

import androidx.core.util.Consumer;

import com.hup.minicontroller.common.util.Supplier;

import java.lang.reflect.Method;

import lombok.Setter;

/**
 * 控制设备的工具类,实现功能:触摸事件,按下按钮事件<pre>
 * .部分方法在高版本系统中已不适用!!如打开[最近应用页面]
 * .需要配置成[系统app],如果无权限,则能在本app界面内操作,app外的操作将会报错(被捕获)
 * .AndroidManifest
 *      android:sharedUserId="android.uid.system"
 * .gradle
 *      android {
 *          signingConfigs {
 *              debug {
 *                  storeFile file('xxxx/xxxx.keystore')
 *              }
 *          }
 *
 * 使用 {@link AccessibilityService}:
 * .在非系统应用时, 依靠无障碍服务实现全局触摸和按键事件
 * .触摸事件是畸形的!!! 尤其是拖拽事件,暂无完善的办法; 无障碍的模拟触摸功能,本是'模拟完整的触摸操作',不支持'实时指定按下拖拽抬起'
 * .使用无障碍服务, (每次启动时)需要用户开启app的无障碍服务
 */
public class ActionMocker {
    private static final String TAG = ActionMocker.class.getSimpleName();

    private final Instrumentation instrumentation;
    private final Context context;
    private final Handler subHandler;
    /**
     * notNull-使用无障碍服务实现功能
     */
    @Setter
    private Supplier<AccessibilityService> accessibilityServiceSupplier;

    /**
     * @param subHandler 子线程Handler,某些操作需要用子线程执行
     */
    public ActionMocker(Context context, Handler subHandler) {
        instrumentation = new Instrumentation();
        this.context = context;
        this.subHandler = subHandler;
    }

    private boolean tryAccessibilityService(Consumer<AccessibilityService> consumer) {
        if (accessibilityServiceSupplier == null) return false;
        AccessibilityService accessibilityService = accessibilityServiceSupplier.get();
        if (accessibilityService == null) return false;
        consumer.accept(accessibilityService);
        return true;
    }

    public void click(int x, int y) {
        subHandler.post(() -> {
            try {
                if (!tryAccessibilityService(service -> {
                    //Log.i(TAG, "tryAccessibilityService.click: [" + x + "," + y + "]");
                    if (VERSION.SDK_INT >= VERSION_CODES.O) {
                        Path path = new Path();
                        path.moveTo(x, y);
                        GestureDescription gesture = new GestureDescription.Builder()
                                .addStroke(new StrokeDescription(path, 0, 20, false))
                                .build();
                        service.dispatchGesture(gesture, null, null);
                    }
                })) {
                    instrumentation.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
                            MotionEvent.ACTION_DOWN, x, y, 0));
                    Thread.sleep(20);
                    instrumentation.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
                            MotionEvent.ACTION_UP, x, y, 0));
                }
            } catch (Exception e) {
                Log.e(TAG, "click: " + e);
            }
        });
    }

    private Integer lastPressX;
    private Integer lastPressY;
    private Long lastPressTime;

    public void press(int x, int y) {
        subHandler.post(() -> {
            try {
                if (!tryAccessibilityService(service -> {
                    //Log.i(TAG, "tryAccessibilityService.press: [" + x + "," + y + "]");
                    if (VERSION.SDK_INT >= VERSION_CODES.O) {
                        lastPressX = x;
                        lastPressY = y;
                        lastPressTime = System.currentTimeMillis();
                        Path path = new Path();
                        path.moveTo(x, y);
                        GestureDescription gesture = new GestureDescription.Builder()
                                .addStroke(new StrokeDescription(path, 0, 1, true))//willContinue=true,动作完成后'不抬起手指'
                                .build();
                        service.dispatchGesture(gesture, null, null);
                    }
                })) {
                    instrumentation.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
                            MotionEvent.ACTION_DOWN, x, y, 0));
                }
            } catch (Exception e) {
                Log.e(TAG, "press: " + e);
            }
        });
    }


    public void up(int x, int y) {
        subHandler.post(() -> {
            try {
                if (!tryAccessibilityService(service -> {
                    //Log.i(TAG, "tryAccessibilityService.up: [" + x + "," + y + "]");
                    if (VERSION.SDK_INT >= VERSION_CODES.O) {
                        Path path = new Path();
                        if (lastPressX == null) lastPressX = x;
                        if (lastPressY == null) lastPressY = y;
                        path.moveTo(lastPressX, lastPressY);
                        path.lineTo(x, y);
                        GestureDescription gesture = new GestureDescription.Builder()
                                .addStroke(new StrokeDescription(path, 0,
                                        lastPressTime == null ? 1 : System.currentTimeMillis() - lastPressTime,
                                        false))
                                .build();
                        service.dispatchGesture(gesture, null, null);
                    }
                })) {
                    instrumentation.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
                            MotionEvent.ACTION_UP, x, y, 0));
                }
            } catch (Exception e) {
                Log.e(TAG, "up: " + e);
            }
        });
    }


    public void move(int x, int y) {
        subHandler.post(() -> {
            try {
                if (!tryAccessibilityService(service -> {
                    //不能这样,效果是连续点击
                    //Log.i(TAG, "tryAccessibilityService.move: [" + x + "," + y + "]");
                    //if (VERSION.SDK_INT >= VERSION_CODES.O) {
                    //    Path path = new Path();
                    //    path.moveTo(x, y);
                    //    path.lineTo(x, y);
                    //    GestureDescription gesture = new GestureDescription.Builder()
                    //            .addStroke(new StrokeDescription(path, 0, 1, true))
                    //            .build();
                    //    service.dispatchGesture(gesture, null, null);
                    //}
                })) {
                    instrumentation.sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(),
                            MotionEvent.ACTION_MOVE, x, y, 0));
                }
            } catch (Exception e) {
                Log.e(TAG, "move: " + e);
            }
        });
    }


    public void buttonBack() {
        subHandler.post(() -> {
            try {
                if (!tryAccessibilityService(service ->
                        service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK))) {
                    instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
                }
            } catch (Exception e) {
                Log.e(TAG, "buttonBack: " + e);
            }
        });
    }

    public void buttonHome() {
        try {
            if (!tryAccessibilityService(service ->
                    service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME))) {
                Intent homeIntent = new Intent(Intent.ACTION_MAIN);
                homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                homeIntent.addCategory(Intent.CATEGORY_HOME);
                context.startActivity(homeIntent);
                if (Build.VERSION.SDK_INT > VERSION_CODES.M) {
                    Log.w(TAG, "buttonHome: 非当前app时,系统版本未支持操作");
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "buttonHome: " + e);
        }
    }


    @SuppressWarnings({"unchecked", "ConstantConditions", "RedundantArrayCreation"})
    public void buttonApp() {
        try {
            if (!tryAccessibilityService(service ->
                    service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS))) {
                if (Build.VERSION.SDK_INT < VERSION_CODES.M) {
                    //版本号是乱写的,安卓7.1的系统异常:找不到方法com.android.internal.statusbar.IStatusBarService.toggleRecentApps
                    @SuppressLint("PrivateApi") Class serviceManagerClass = Class.forName("android.os.ServiceManager");
                    Method getService = serviceManagerClass.getMethod("getService", String.class);
                    IBinder iBinder = (IBinder) getService.invoke(serviceManagerClass, "statusbar");
                    Class statusBarClass = Class.forName(iBinder.getInterfaceDescriptor());
                    Method clearAll = statusBarClass.getMethod("toggleRecentApps");
                    clearAll.setAccessible(true);
                    Object statusBarObject = statusBarClass.getClasses()[0].getMethod("asInterface", IBinder.class)
                            .invoke(null, new Object[]{iBinder});
                    clearAll.invoke(statusBarObject);
                } else {
//                ComponentName componentName = new ComponentName("com.android.systemui",
//                        "com.android.systemui.recents.RecentsActivity");
//                Intent intent = new Intent();
//                intent.setComponent(componentName);
//                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
//                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
//                context.startActivity(intent);
                /*
                不能用了???
                https://blog.csdn.net/hacker_crazy/article/details/78399303
                 */
                    Log.e(TAG, "buttonApp: 系统版本未支持操作");
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "buttonApp: " + e);
        }
    }

}
